package com.zqh.myfmmall.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zqh.myfmmall.WxPay.WxPayService;
import com.zqh.myfmmall.constants.OrderCloseType;
import com.zqh.myfmmall.constants.OrderStatus;
import com.zqh.myfmmall.constants.PayType;
import com.zqh.myfmmall.dao.OrdersDao;
import com.zqh.myfmmall.entity.*;
import com.zqh.myfmmall.redis.RedisUtil;
import com.zqh.myfmmall.service.*;
import com.zqh.myfmmall.vo.OrdersVO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单 (Orders)表服务实现类
 *
 * @author KONO 曾淇杭哒！
 * @since 2022-01-20 20:53:00
 */
@Service("ordersService")
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersDao, Orders> implements OrdersService{
    @Resource
    private ShoppingCartService shoppingCartService;
    @Resource
    private UserAddrService userAddrService;
    @Resource
    private ProductSkuService productSkuService;
    @Resource
    private ProductService productService;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private ProductImgService productImgService;
    @Resource
    private WxPayService wxPayService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    @Transactional
    public Map<String, String> addOrder(List<String> shoppingCartIds, String addrId, String userId) throws RuntimeException {
        log.info("add order begin...");
        HashMap<String, String> orderinfoMap = null;
        //保存已经被加锁的套餐id及其对应的redisson锁，在最后用于释放已加锁的套餐
        HashMap<String, RLock> redissonLockMap = new HashMap<>();
        //验证库存
        List<ShoppingCart> shoppingCarts = shoppingCartService.listByIds(shoppingCartIds);
        try {
            if (verifyStock(shoppingCarts)) {
                //对商品加分布式锁
                List<String> skuIds = shoppingCarts.stream().map(shoppingCart -> shoppingCart.getSkuId()).collect(Collectors.toList());
                boolean isLocked = true;
                for (String skuId : skuIds) {
                    RLock lock = redissonClient.getLock(skuId);
                    //添加该套餐的分布式锁（非阻塞锁），3秒重试时间，20s过期时间
                    boolean lockSuccess = lock.tryLock(3, 20, TimeUnit.SECONDS);
                    //只要有一个商品加锁失败那么整体就是加锁失败（这里只要有一次失败后islocked就会一直为false）
                    isLocked = isLocked && lockSuccess;
                    if (lockSuccess) {
                        redissonLockMap.put(lock.getName(), lock);
                    }
                }
                //整体加锁成功
                if (isLocked) {
                    //再次检验库存，以防在加锁期间库存发送改变，第一次验证库存是为了避免库存不足还进行redis加锁等操作浪费资源
                    if (verifyStock(shoppingCarts)) {
                        UserAddr addr = userAddrService.getById(addrId);
                        Orders order = new Orders();
                        String orderId = UUID.randomUUID().toString();
                        order.setOrderId(orderId);

                        //生成订单中的地址快照
                        order.setReceiverAddress(getAddressJsonString(addr));
                        order.setReceiverMobile(addr.getReceiverMobile());
                        order.setReceiverName(addr.getReceiverName());

                        order.setUserId(userId);
                        order.setPayType(PayType.WECHATPAY);
                        order.setStatus(OrderStatus.NOT_PAYED);

                        //订单商品名称，用，分隔，发起支付微信等支付请求时作为商品描述参数
                        String productNames = "";
                        BigDecimal orderTotalAmount = new BigDecimal(0);
                        //生成订单项
                        for (ShoppingCart shoppingCart : shoppingCarts) {
                            OrderItem orderItem = new OrderItem();
                            String productId = shoppingCart.getProductId();
                            Product product = productService.getById(productId);
                            String skuId = shoppingCart.getSkuId();
                            ProductSku sku = productSkuService.getById(skuId);
                            productNames += product.getProductName() + ",";
                            orderItem.setOrderId(order.getOrderId());
                            orderItem.setProductId(productId);
                            //保存商品快照
                            orderItem.setProductName(product.getProductName());
                            orderItem.setProductImg(productImgService.getMainImgByProductId(productId));
                            orderItem.setSkuId(skuId);
                            //保存套餐快照
                            orderItem.setSkuName(sku.getSkuName());
                            orderItem.setProductPrice(sku.getSellPrice());

                            orderItem.setBuyCounts(shoppingCart.getCartNum());
                            BigDecimal orderItemTotalAmount = new BigDecimal(String.valueOf(orderItem.getProductPrice()
                                    .multiply(new BigDecimal(String.valueOf(orderItem.getBuyCounts())))));
                            orderTotalAmount.add(orderItemTotalAmount);
                            orderItem.setTotalAmount(orderItemTotalAmount);
                            orderItem.setIsComment(0);
                            orderItemService.save(orderItem);

                            //减去库存
                            sku.setStock(sku.getStock() - shoppingCart.getCartNum());
                            productSkuService.updateById(sku);

                            //删除购物车项
                            shoppingCartService.removeById(shoppingCart.getCartId());
                        }
                        order.setTotalAmount(orderTotalAmount);
                        //暂时没有加入优惠卷等功能故实际价格等于订单总价
                        order.setActualAmount(orderTotalAmount);
                        //将字符串最后的一个，去掉
                        productNames.substring(0, productNames.length());
                        order.setProductNames(productNames);
                        //保存订单
                        save(order);

                        //删除购物车
                        shoppingCartService.removeByIds(shoppingCartIds);
                        orderinfoMap = new HashMap<>();
                        orderinfoMap.put("orderId", order.getOrderId());
                        orderinfoMap.put("productNames", order.getProductNames());
                        orderinfoMap.put("actualAmount", String.valueOf(order.getActualAmount()));
                        log.info("add order success : " + orderinfoMap.get("orderId"));
                    }
                }
            }
        } catch (Exception e) {
            log.warn("添加订单出现异常-" + e);
            //抛出异常用于spring捕捉进行事务回滚
            throw new RuntimeException();
            //最后必定释放锁
        } finally {
            Collection<RLock> redissonLocks = redissonLockMap.values();
            for (RLock redissonLock : redissonLocks) {
                redissonLock.unlock();
            }
        }
        //添加订单失败则返回的是null，可能原因：检验库存失败，加锁失败
        return orderinfoMap;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void checkExpiredNotPayed(String orderId) throws RuntimeException {
        Orders order = query().eq("order_id", orderId).select("order_id", "status").one();
        try {
            //访问微信支付接口查看订单是否已支付
            if (!wxPayService.checkPayed(orderId)) {
                //释放订单的商品资源
                List<OrderItem> orderItems = orderItemService.getByOrderId(orderId);
                for (OrderItem orderItem : orderItems) {
                    ProductSku sku = productSkuService.query().eq("sku_id", orderItem.getProductId()).select("sku_id", "stock").one();
                    sku.setStock(sku.getStock() + orderItem.getBuyCounts());
                    productSkuService.updateById(sku);
                }
                //修改订单状态并设置关闭类型为超时未支付
                order.setStatus(OrderStatus.FINISHED);
                order.setCloseType(OrderCloseType.EXPIRED_NOTPAYED);
                updateById(order);
            }
        } catch (Exception e) {
            log.info("访问wx支付接口查到订单是否已支付出现异常");
            //抛出一个异常，这样spring才能发现并对事务进行滚，不然需要自己手动进行回滚，具体细节看笔记
            throw new RuntimeException();
        }
    }

    @Override
    public List<OrdersVO> pageByUserIdAndStatus(String userId, Integer status, Integer pageSize, Integer pageNum) {
        List<OrdersVO> ordersVOS = new ArrayList<>();
        Page<Orders> ordersPage = new Page<>(pageNum, pageSize);
        QueryWrapper<Orders> ordersQueryWrapper = new QueryWrapper<>();
        ordersQueryWrapper.eq("user_id", userId).eq("status", status);
        page(ordersPage, ordersQueryWrapper);
        List<Orders> orders = ordersPage.getRecords();
        for (Orders order : orders) {
            List<OrderItem> orderItems = orderItemService.getByOrderId(order.getOrderId());
            OrdersVO ordersVO = new OrdersVO(order, orderItems);
            ordersVOS.add(ordersVO);
        }
        return ordersVOS;
    }

    @Override
    public Map<String, Integer> orderCounts(String userId) {
        HashMap<String, Integer> countsMap = new HashMap<>();
        Integer notpayedCount = query().eq("user_id", userId).eq("status", OrderStatus.NOT_PAYED).count();
        Integer notDeliveredCount = query().eq("user_id", userId).eq("status", OrderStatus.NOT_DELIVERED).count();
        Integer notEvaluatedCount = query().eq("user_id", userId).eq("status", OrderStatus.NOT_EVALUATED).count();
        Integer notReceivedCount = query().eq("user_id", userId).eq("status", OrderStatus.NOT_RECEIVED).count();
        Integer finishedCount = query().eq("user_id", userId).eq("status", OrderStatus.FINISHED).count();
        Integer closedCount = query().eq("user_id", userId).eq("status", OrderStatus.CLOSED).count();
        countsMap.put(OrderStatus.NOT_PAYED.toString(), notpayedCount);
        countsMap.put(OrderStatus.NOT_DELIVERED.toString(), notDeliveredCount);
        countsMap.put(OrderStatus.NOT_EVALUATED.toString(), notEvaluatedCount);
        countsMap.put(OrderStatus.NOT_RECEIVED.toString(), notReceivedCount);
        countsMap.put(OrderStatus.FINISHED.toString(), finishedCount);
        countsMap.put(OrderStatus.CLOSED.toString(), closedCount);
        return countsMap;
    }


    public String getAddressJsonString(UserAddr addr) {
        HashMap<String, String> addrMap = new HashMap<>();
        ObjectMapper objectMapper = new ObjectMapper();

        addrMap.put("province", addr.getProvince());
        addrMap.put("city", addr.getCity());
        addrMap.put("area", addr.getArea());
        addrMap.put("addr", addr.getAddr());
        try {
            return objectMapper.writeValueAsString(addrMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean verifyStock(List<ShoppingCart> shoppingCarts) {
        for (ShoppingCart shoppingCart : shoppingCarts) {
            String skuId = shoppingCart.getSkuId();
            ProductSku sku = productSkuService.query().eq("sku_id", skuId).select("stock").one();
            if (sku.getStock() < shoppingCart.getCartNum()) {
                return false;
            }
        }
        return true;
    }
}

